﻿using System;
using System.Security.Principal;
using System.Runtime.InteropServices;

namespace Alex.Library.Log
{
    /// <summary>
    /// C#模拟Windows用户
    /// 模拟某个用户的权限
    /// 调用结束后自动恢复为原先用户。前提是使用using。
    /// 或者手动恢复。需要调用.Dispose()方法。
    /// 使用方法如下：
    /// using (ImpersonateAccount sa = new ImpersonateAccount("用户名", "域", "密码"))
    /// { 
    ///     //在这里输入上面这位同志要执行的代码。 
    /// }
    /// </summary>
    public class ImpersonateAccount : IDisposable
    {
        private WindowsIdentity current;

        private const int LOGON32_LOGON_INTERACTIVE = 2;

        private const int LOGON32_PROVIDER_DEFAULT = 0;

        private WindowsImpersonationContext impersonationContext;

        [DllImport("advapi32.dll")]
        private static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);

        /// <summary>
        /// 模拟指定用户
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="domain">域</param>
        /// <param name="password">密码</param>
        /// <returns>true/false</returns>
        public ImpersonateAccount(String userName, String domain, String password)
        {
            // 保存当前用户
            current = WindowsIdentity.GetCurrent();

            WindowsIdentity tempWindowsIdentity;

            IntPtr token = IntPtr.Zero;

            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                        }
                    }
                }
            }

            if (token != IntPtr.Zero)
            {
                CloseHandle(token);
            }

            if (tokenDuplicate != IntPtr.Zero)
            {
                CloseHandle(tokenDuplicate);
            }
        }

        /// <summary>
        /// 撤销模拟,返回为操作前的用户权限。
        /// </summary>
        public void Dispose()
        {
            impersonationContext.Undo();
        }

        /// <summary>
        /// 返回为执行模拟操作前的用户
        /// </summary>
        private void Undo()
        {
            if (current != null)
            {
                current.Impersonate();
                current = null;
            }
        }
    }
}
